

//================================================================================
LOCO_FORCE_INLINE Matrix4::Matrix4(Vector4 const& column0, Vector4 const& column1, Vector4 const& column2, Vector4 const& column3)
{
	m[0][0] = column0.x; m[0][1] = column1.x; m[0][2] = column2.x; m[0][3] = column3.x;
	m[1][0] = column0.y; m[1][1] = column1.y; m[1][2] = column2.y; m[1][3] = column3.y;
	m[2][0] = column0.z; m[2][1] = column1.z; m[2][2] = column2.z; m[2][3] = column3.z;
	m[3][0] = column0.w; m[3][1] = column1.w; m[3][2] = column2.w; m[3][3] = column3.w;
}

//================================================================================
LOCO_FORCE_INLINE Matrix4::Matrix4(	float m00, float m01, float m02, float m03,
										float m10, float m11, float m12, float m13,
										float m20, float m21, float m22, float m23,
										float m30, float m31, float m32, float m33)
{
	m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03;
	m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13;
	m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23;
	m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33;
}

//================================================================================
LOCO_FORCE_INLINE Matrix4 Matrix4::operator+(Matrix4 const& in) const
{
	Matrix4 result;

	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			result.m[y][x] = m[y][x] + in.m[y][x];
		}
	}

	return result;
}

//================================================================================
LOCO_FORCE_INLINE Matrix4 Matrix4::operator-(Matrix4 const& in) const
{
	Matrix4 result;

	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			result.m[y][x] = m[y][x] - in.m[y][x];
		}
	}

	return result;
}

//================================================================================
LOCO_FORCE_INLINE Matrix4 Matrix4::operator*(Matrix4 const& in) const
{
	Matrix4 result;

	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			result.m[y][x] = 0.0f;
			for (uint32 i=0; i<4; i++)
			{
				result.m[y][x] += m[y][i] * in.m[i][x];
			}	
		}
	}

	return result;
}

//================================================================================
LOCO_FORCE_INLINE Vector4 Matrix4::operator*(Vector4 const& in) const
{
	Vector4 result(0.0f, 0.0f, 0.0f, 0.0f);

	result.x += m[0][0] * in.x;
	result.x += m[0][1] * in.y;
	result.x += m[0][2] * in.z;
	result.x += m[0][3] * in.w;

	result.y += m[1][0] * in.x;
	result.y += m[1][1] * in.y;
	result.y += m[1][2] * in.z;
	result.y += m[1][3] * in.w;

	result.z += m[2][0] * in.x;
	result.z += m[2][1] * in.y;
	result.z += m[2][2] * in.z;
	result.z += m[2][3] * in.w;

	result.w += m[3][0] * in.x;
	result.w += m[3][1] * in.y;
	result.w += m[3][2] * in.z;
	result.w += m[3][3] * in.w;

	return result;
}

//================================================================================
LOCO_FORCE_INLINE Matrix4& Matrix4::operator*=(Matrix4 const& in)
{
	*this = *this * in;
	return *this;
}

//================================================================================
LOCO_FORCE_INLINE bool Matrix4::operator==(Matrix4 const& in) const
{
	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			if (m[y][x] != in.m[y][x])
			{
				return false;
			}
		}
	}

	return true;
}

//================================================================================
LOCO_FORCE_INLINE bool Matrix4::operator!=(Matrix4 const& in) const
{
	return !(*this == in);
}

//================================================================================
LOCO_FORCE_INLINE Matrix4 Matrix4::transpose() const
{
	Matrix4 result;

	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			result.m[y][x] = m[x][y];
		}
	}
	return result;
}

inline float Matrix4::determinant() const
{
	return	m[0][0] * (
		m[1][1] * (m[2][2] * m[3][3] - m[2][3] * m[3][2]) -
		m[2][1] * (m[1][2] * m[3][3] - m[1][3] * m[3][2]) +
		m[3][1] * (m[1][2] * m[2][3] - m[1][3] * m[2][2])
		) -
		m[1][0] * (
		m[0][1] * (m[2][2] * m[3][3] - m[2][3] * m[3][2]) -
		m[2][1] * (m[0][2] * m[3][3] - m[0][3] * m[3][2]) +
		m[3][1] * (m[0][2] * m[2][3] - m[0][3] * m[2][2])
		) +
		m[2][0] * (
		m[0][1] * (m[1][2] * m[3][3] - m[1][3] * m[3][2]) -
		m[1][1] * (m[0][2] * m[3][3] - m[0][3] * m[3][2]) +
		m[3][1] * (m[0][2] * m[1][3] - m[0][3] * m[1][2])
		) -
		m[3][0] * (
		m[0][1] * (m[1][2] * m[2][3] - m[1][3] * m[2][2]) -
		m[1][1] * (m[0][2] * m[2][3] - m[0][3] * m[2][2]) +
		m[2][1] * (m[0][2] * m[1][3] - m[0][3] * m[1][2])
		);
}

inline Matrix4 Matrix4::inverse() const
{
	Matrix4 result;
	
	float fTmp[4][4];
	float fTmpDet[4];

	fTmp[0][0] = m[2][2] * m[3][3] - m[2][3] * m[3][2];
	fTmp[0][1] = m[1][2] * m[3][3] - m[1][3] * m[3][2];
	fTmp[0][2] = m[1][2] * m[2][3] - m[1][3] * m[2][2];

	fTmp[1][0] = m[2][2] * m[3][3] - m[2][3] * m[3][2];
	fTmp[1][1] = m[0][2] * m[3][3] - m[0][3] * m[3][2];
	fTmp[1][2] = m[0][2] * m[2][3] - m[0][3] * m[2][2];

	fTmp[2][0] = m[1][2] * m[3][3] - m[1][3] * m[3][2];
	fTmp[2][1] = m[0][2] * m[3][3] - m[0][3] * m[3][2];
	fTmp[2][2] = m[0][2] * m[1][3] - m[0][3] * m[1][2];

	fTmp[3][0] = m[1][2] * m[2][3] - m[1][3] * m[2][2];
	fTmp[3][1] = m[0][2] * m[2][3] - m[0][3] * m[2][2];
	fTmp[3][2] = m[0][2] * m[1][3] - m[0][3] * m[1][2];

	fTmpDet[0] = m[1][1] * fTmp[0][0] - m[2][1] * fTmp[0][1] + m[3][1] * fTmp[0][2];
	fTmpDet[1] = m[0][1] * fTmp[1][0] - m[2][1] * fTmp[1][1] + m[3][1] * fTmp[1][2];
	fTmpDet[2] = m[0][1] * fTmp[2][0] - m[1][1] * fTmp[2][1] + m[3][1] * fTmp[2][2];
	fTmpDet[3] = m[0][1] * fTmp[3][0] - m[1][1] * fTmp[3][1] + m[2][1] * fTmp[3][2];

	float fDet = m[0][0] * fTmpDet[0] - m[1][0] * fTmpDet[1] + m[2][0] * fTmpDet[2] - m[3][0] * fTmpDet[3];
	float fInvDet = 1.0f / fDet;

	result.m[0][0] = fInvDet * fTmpDet[0];
	result.m[0][1] = -fInvDet * fTmpDet[1];
	result.m[0][2] = fInvDet * fTmpDet[2];
	result.m[0][3] = -fInvDet * fTmpDet[3];
	result.m[1][0] = -fInvDet * (m[1][0] * fTmp[0][0] - m[2][0] * fTmp[0][1] + m[3][0] * fTmp[0][2]);
	result.m[1][1] = fInvDet * (m[0][0] * fTmp[1][0] - m[2][0] * fTmp[1][1] + m[3][0] * fTmp[1][2]);
	result.m[1][2] = -fInvDet * (m[0][0] * fTmp[2][0] - m[1][0] * fTmp[2][1] + m[3][0] * fTmp[2][2]);
	result.m[1][3] = fInvDet * (m[0][0] * fTmp[3][0] - m[1][0] * fTmp[3][1] + m[2][0] * fTmp[3][2]);
	result.m[2][0] = fInvDet * (
		m[1][0] * (m[2][1] * m[3][3] - m[2][3] * m[3][1]) -
		m[2][0] * (m[1][1] * m[3][3] - m[1][3] * m[3][1]) +
		m[3][0] * (m[1][1] * m[2][3] - m[1][3] * m[2][1])
		);
	result.m[2][1] = -fInvDet * (
		m[0][0] * (m[2][1] * m[3][3] - m[2][3] * m[3][1]) -
		m[2][0] * (m[0][1] * m[3][3] - m[0][3] * m[3][1]) +
		m[3][0] * (m[0][1] * m[2][3] - m[0][3] * m[2][1])
		);
	result.m[2][2] = fInvDet * (
		m[0][0] * (m[1][1] * m[3][3] - m[1][3] * m[3][1]) -
		m[1][0] * (m[0][1] * m[3][3] - m[0][3] * m[3][1]) +
		m[3][0] * (m[0][1] * m[1][3] - m[0][3] * m[1][1])
		);
	result.m[2][3] = -fInvDet * (
		m[0][0] * (m[1][1] * m[2][3] - m[1][3] * m[2][1]) -
		m[1][0] * (m[0][1] * m[2][3] - m[0][3] * m[2][1]) +
		m[2][0] * (m[0][1] * m[1][3] - m[0][3] * m[1][1])
		);
	result.m[3][0] = -fInvDet * (
		m[1][0] * (m[2][1] * m[3][2] - m[2][2] * m[3][1]) -
		m[2][0] * (m[1][1] * m[3][2] - m[1][2] * m[3][1]) +
		m[3][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
		);
	result.m[3][1] = fInvDet * (
		m[0][0] * (m[2][1] * m[3][2] - m[2][2] * m[3][1]) -
		m[2][0] * (m[0][1] * m[3][2] - m[0][2] * m[3][1]) +
		m[3][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1])
		);
	result.m[3][2] = -fInvDet * (
		m[0][0] * (m[1][1] * m[3][2] - m[1][2] * m[3][1]) -
		m[1][0] * (m[0][1] * m[3][2] - m[0][2] * m[3][1]) +
		m[3][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])
		);
	result.m[3][3] = fInvDet * (
		m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
		m[1][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1]) +
		m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])
		);

	return result;
}

//================================================================================
inline bool Matrix4::near_equal(Matrix4 const& a, Matrix4 const& b, float delta)
{
	for (uint32 y=0; y<4; y++)
	{
		for (uint32 x=0; x<4; x++)
		{
			if (!loco::near_equal(a.m[y][x], b.m[y][x], delta))
			{
				return false;
			}
		}
	}
	return true;
}

